package edu.drexel.cs575.phw1.scale;

import java.text.DecimalFormat;

import listeners.IdleTimerListener;
import listeners.KeypadListener;
import listeners.PowerLevelListener;
import listeners.WeightMechanismListener;
import edu.drexel.cs575.phw1.scale.exception.ScaleException;

/**
 * Digital implementation of a scale
 * 
 * @author anthony
 *
 */
public class DigitalScale implements PowerLevelListener, WeightMechanismListener, KeypadListener, IdleTimerListener {
	private static final int MINIMUM_POWER_LEVEL = 120;
	private static final int MAXIMUM_POWER_LEVEL = 130;
	private boolean poweredOn;
	private Display display;
	private WeightMechanism weightMechanism;
	private Keypad keypad;
	private AudioDevice audioDevice;
	private IdleTimer idleTimer;
	
	public void powerOn(Power power) throws ScaleException {
		if (power == null) {
			throw new IllegalArgumentException("Power cannot be null.");
		}

		if (!isPowerValid(power)) {
			throw new ScaleException("Power isn't valid for scale. Could not power on.");
		}
		
		init(power);
		
		System.out.println("Power: ON");
	}
	
	public void powerOff() {
		this.poweredOn = false;
		
		this.idleTimer.stop();
		audioDevice.beep();
		System.out.println("Power: OFF");
	}
	
	/**
	 * Adds a new item to be weighed to the scale.
	 * 
	 * @param item
	 * @throws ScaleException
	 */
	public void addItem(Weighable item) throws ScaleException {
		if (item == null) {
			throw new IllegalArgumentException("Item cannot be null");
		}
		
		if (!this.poweredOn) {
			throw new ScaleException("Scale is not powered on. Cannot weigh item.");
		}
		
		this.weightMechanism.addItem(item);
		this.idleTimer.reset();
	}
	
	public void pushButton(Button button) {
		audioDevice.beep();
		
		this.keypad.pushButton(button);
		this.idleTimer.reset();
	}
	
	public void removeItems() {
		this.weightMechanism.removeItems();
		this.idleTimer.reset();
	}
	
	public void setTimeout(int timeout) {
		//Cheating a little here. Going to allow the timeout to be set
		//directly using a method call instead of via the keypad.
		this.idleTimer.setTimeout(timeout);
	}
	
	// -- Private methods
	
	private boolean isPowerValid(Power power) {
		if (power == null) {
			return false;
		}
		
		int powerLevel = power.getPowerLevel();
		
		if (powerLevel < MINIMUM_POWER_LEVEL || powerLevel > MAXIMUM_POWER_LEVEL) {
			return false;
		}
		
		return true;
	}
	
	private void init(Power power) {
		this.poweredOn = true;
		this.display = new Display();
		this.weightMechanism = new WeightMechanism();
		this.keypad = new Keypad();
		this.audioDevice = new AudioDevice();
		this.idleTimer = new IdleTimer();
		
		power.addListener(this);
		this.weightMechanism.addListener(this);
		this.keypad.addListener(this);
		this.idleTimer.addListener(this);
		audioDevice.beep();
	}

	// -- Implement listeners
	
	@Override
	public void powerLevelChanged(Power power) {
		if (!isPowerValid(power)) {
			powerOff();
		}		
	}

	@Override
	public void itemAdded() {
		Weight totalWeight = weightMechanism.getTotalWeight();
		DecimalFormat df = new DecimalFormat("#.##");
		String displayValue = String.format("%s %s", df.format(totalWeight.getValue()), totalWeight.getUnitOfMeasure().getUnitType());
		
		this.audioDevice.beep();
		this.display.show(displayValue);		
	}

	@Override
	public void itemsRemoved() {
		this.display.clear();
	}

	@Override
	public void tareSet() {
		this.display.clear();
	}

	@Override
	public void unitsButtonPushed() {
		this.weightMechanism.toggleUnits();	
		
		itemAdded();
	}

	@Override
	public void tareButtonPushed() {
		this.weightMechanism.setTare();
	}

	@Override
	public void timeExpired() {
		powerOff();		
	}

	@Override
	public void beepButtonPushed() {
		this.audioDevice.toggleBeep();
	}
	
}
 